package service

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strconv"
	"strings"
	"time"
	"trace-rule/internal/domain/model"
	"trace-rule/pkg/config"

	"gopkg.in/gomail.v2"

	"github.com/eclipse/paho.mqtt.golang"
)

type PriceMonitorService struct {
	ruleService   *RuleService
	mqttClient    mqtt.Client
	lastEmailSent map[string]time.Time
}

type BinanceTickerResponse []struct {
	Symbol             string `json:"symbol"`
	PriceChange        string `json:"priceChange"`
	PriceChangePercent string `json:"priceChangePercent"`
	WeightedAvgPrice   string `json:"weightedAvgPrice"`
	PrevClosePrice     string `json:"prevClosePrice"`
	LastPrice          string `json:"lastPrice"`
	LastQty            string `json:"lastQty"`
	BidPrice           string `json:"bidPrice"`
	BidQty             string `json:"bidQty"`
	AskPrice           string `json:"askPrice"`
	AskQty             string `json:"askQty"`
	OpenPrice          string `json:"openPrice"`
	HighPrice          string `json:"highPrice"`
	LowPrice           string `json:"lowPrice"`
	Volume             string `json:"volume"`
	QuoteVolume        string `json:"quoteVolume"`
	OpenTime           int64  `json:"openTime"`
	CloseTime          int64  `json:"closeTime"`
}

func NewPriceMonitorService(ruleService *RuleService) *PriceMonitorService {
	mqttCfg := config.GlobalConfig.MQTT
	opts := mqtt.NewClientOptions()
	opts.AddBroker(mqttCfg.Broker)
	opts.SetUsername(mqttCfg.Username)
	opts.SetPassword(mqttCfg.Password)

	client := mqtt.NewClient(opts)
	if token := client.Connect(); token.Wait() && token.Error() != nil {
		panic(token.Error())
	}

	return &PriceMonitorService{
		ruleService:   ruleService,
		mqttClient:    client,
		lastEmailSent: make(map[string]time.Time),
	}
}

func (s *PriceMonitorService) StartMonitoring() {
	// test
	ticker := time.NewTicker(10 * time.Second)
	//ticker := time.NewTicker(5 * time.Minute)
	go func() {
		for range ticker.C {
			s.checkPrices()
		}
	}()
}

func (s *PriceMonitorService) checkPrices() {
	// 1. 获取启用的规则
	rules, err := s.ruleService.ListEnabledRules()
	if err != nil {
		fmt.Printf("Error getting enabled rules: %v\n", err)
		return
	}

	// 提取symbols
	var symbols []string
	for _, rule := range rules {
		symbols = append(symbols, rule.Symbol)
	}

	// 2. 调用Binance API
	symbolsJSON, _ := json.Marshal(symbols)
	url := fmt.Sprintf("https://data-api.binance.vision/api/v3/ticker/24hr?symbols=%s", string(symbolsJSON))

	resp, err := http.Get(url)
	if err != nil {
		fmt.Printf("Error calling Binance API: %v\n", err)
		return
	}
	defer resp.Body.Close()

	var tickerData BinanceTickerResponse
	if err := json.NewDecoder(resp.Body).Decode(&tickerData); err != nil {
		fmt.Printf("Error decoding response: %v\n", err)
		return
	}

	// 3. 发送到MQTT
	messageJSON, _ := json.Marshal(tickerData)
	token := s.mqttClient.Publish("/sys/crypto/price", 0, false, messageJSON)
	token.Wait()

	for _, ticker := range tickerData {
		for _, rule := range rules {
			if ticker.Symbol == rule.Symbol {
				if s.shouldSendAlert(rule, ticker.LastPrice) {
					s.sendEmail(rule.TargetEmails, rule.EmailTemplate, ticker.LastPrice)
					s.lastEmailSent[rule.Symbol] = time.Now()
				}
			}
		}
	}
}

func (s *PriceMonitorService) shouldSendAlert(rule model.Rule, lastPrice string) bool {
	price, _ := strconv.ParseFloat(lastPrice, 64)
	if price < rule.MinValue || price > rule.MaxValue {
		if lastSent, ok := s.lastEmailSent[rule.Symbol]; ok {
			interval, _ := time.ParseDuration(rule.Interval)
			if time.Since(lastSent) < interval {
				return false
			}
		}
		return true
	}
	return false
}

func (s *PriceMonitorService) sendEmail(targetEmails string, template string, lastPrice string) {
	cfg := config.GlobalConfig.Email
	from := cfg.Username
	password := cfg.Password
	subject := cfg.Subject
	body := fmt.Sprintf(template, lastPrice)

	emails := strings.Split(targetEmails, ",")
	d := gomail.NewDialer(cfg.Host, cfg.Port, from, password)

	for _, email := range emails {
		m := gomail.NewMessage()
		m.SetHeader("From", from)
		m.SetHeader("To", email)
		m.SetHeader("Subject", subject)
		m.SetBody("text/plain", body)

		if err := d.DialAndSend(m); err != nil {
			fmt.Printf("Error sending email to %s: %v\n", email, err)
		} else {
			fmt.Printf("发送了邮箱%s\n", email)
		}
	}
}
